﻿#region LicenseInfo
/*
 * WoW Automation
 * Copyright 2008, 2009
 * Michael Sutton
 * 
 * This file is part of Wow Automation.
 * 
 * Wow Automation is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Wow Automation is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Wow Automation.  If not, see <http://www.gnu.org/licenses/>.
*/
#endregion
using System;
using System.Drawing;
using System.Drawing.Imaging;


namespace Automation
{

    //note: this should be updated to remove the Marsheling.   convert this the 100% managed code.   
    public class FastPixel
    {
        private byte[] rgbValues;
        private BitmapData bmpData;
        private IntPtr bmpPtr;
        private bool locked;

        private readonly bool _isAlpha;
        private readonly Bitmap _bitmap;
        private readonly int _width;
        private readonly int _height;

        public int Width
        {
            get
            {
                return _width;
            }
        }

        public int Height
        {
            get
            {
                return _height;
            }
        }

        public bool IsAlphaBitmap
        {
            get
            {
                return _isAlpha;
            }
        }

        public Bitmap Bitmap
        {
            get
            {
                return _bitmap;
            }
        }

        public FastPixel(Bitmap bitmap)
        {
            if (bitmap.PixelFormat == (bitmap.PixelFormat | PixelFormat.Indexed))
                throw new Exception("Cannot lock an Indexed image.");

            _bitmap = bitmap;
            _isAlpha = (Bitmap.PixelFormat == (Bitmap.PixelFormat | PixelFormat.Alpha));
            _width = bitmap.Width;
            _height = bitmap.Height;
        }

        public void Lock()
        {
            if (locked)
                throw new Exception("Bitmap already locked.");

            Rectangle rect = new Rectangle(0, 0, Width, Height);
            bmpData = Bitmap.LockBits(rect, ImageLockMode.ReadWrite, Bitmap.PixelFormat);
            bmpPtr = bmpData.Scan0;

            if (IsAlphaBitmap)
            {
                int bytes = (Width * Height) * 4;
                rgbValues = new byte[bytes];
                System.Runtime.InteropServices.Marshal.Copy(bmpPtr, rgbValues, 0, rgbValues.Length);
            }
            else
            {
                int bytes = (Width * Height) * 3;
                rgbValues = new byte[bytes];
                
                System.Runtime.InteropServices.Marshal.Copy(bmpPtr, rgbValues, 0, rgbValues.Length);
            }
            locked = true;
        }

        public void Unlock(bool setPixels)
        {
            if (!locked)
                throw new Exception("Bitmap not locked.");

            // Copy the RGB values back to the bitmap;
            if (setPixels)
                System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, bmpPtr, rgbValues.Length);

            // Unlock the bits.;
            Bitmap.UnlockBits(bmpData);
            locked = false;
        }

        public void Clear(Color colour)
        {
            if (!locked)
                throw new Exception("Bitmap not locked.");

            if (IsAlphaBitmap)
            {
                for (int index = 0; index < rgbValues.Length; index += 4)
                {
                    rgbValues[index] = colour.B;
                    rgbValues[index + 1] = colour.G;
                    rgbValues[index + 2] = colour.R;
                    rgbValues[index + 3] = colour.A;
                }
            }
            else
            {
                for (int index = 0; index < rgbValues.Length; index += 3)
                {
                    rgbValues[index] = colour.B;
                    rgbValues[index + 1] = colour.G;
                    rgbValues[index + 2] = colour.R;
                }
            }
        }

        public void SetPixel(Point location, Color colour)
        {
            SetPixel(location.X, location.Y, colour);
        }

        public void SetPixel(int x, int y, Color colour)
        {
            if (!locked)
                throw new Exception("Bitmap not locked.");

            if (IsAlphaBitmap)
            {
                int index = ((y * Width + x) * 4);
                rgbValues[index] = colour.B;
                rgbValues[index + 1] = colour.G;
                rgbValues[index + 2] = colour.R;
                rgbValues[index + 3] = colour.A;
            }
            else
            {
                int index = ((y * Width + x) * 3);
                rgbValues[index] = colour.B;
                rgbValues[index + 1] = colour.G;
                rgbValues[index + 2] = colour.R;
            }
        }

        public Color GetPixel(Point location)
        {
            return GetPixel(location.X, location.Y);
        }

        public Color GetPixel(int x, int y)
        {
            if (!locked)
                throw new Exception("Bitmap not locked.");

            if (IsAlphaBitmap)
            {
                int index = ((y * Width + x) * 4);
                int b = rgbValues[index];
                int g = rgbValues[index + 1];
                int r = rgbValues[index + 2];
                int a = rgbValues[index + 3];
                return Color.FromArgb(a, r, g, b);
            }
            else
            {
                int index = ((y * Width + x) * 3);
                int b = rgbValues[index];
                int g = rgbValues[index + 1];
                int r = rgbValues[index + 2];
                return Color.FromArgb(r, g, b);
            }
        }
    }
}
